 # 腾讯证券内容获取

# -使用cookies自动登录
# -以组号在excel中新建一个sheet，命名是当前日（5天）和止盈（1个）
# -依次获取自选股的6个分组，具体见readme
# 开盘时间内每半小时获取一次个股内容（涨跌幅是相对于刚买入时的），在规定时间点存入表格对应sheet对应列
# sheet定位方法和组名要一致
# 每天下午2点对数据进行策略筛选
# 第5日后符合止盈策略的复制进止盈sheet
# 符合策略出的发邮件，策略见readme
# 没有符合策略的也要邮件通知，并自动新建下一天的分组（自动跳过非股票交易日）
# 循环执行上述流程
# -在表格中记录绘制当前价和刚加入组时价格的涨跌幅百分比曲线（后期）
# -策略具体内容查看及修改见对应函数（太复杂暂时不用宏定义，后期可以）

# 需要修改的全局变量或配置：
# 先进行cookies的获取，而且可能需要不定时更新
# 买入和加入组必需同步发生，这样策略时间才准确
# 为了保证当前日名字不冲突，每个月都需要把策略筛选日期外的删除 （后续可以自动删除）
# stock_url 登录地址
# Check_space 间隔读取时间 分钟
# Stock_data_path 数据保存路径
# stock_templet_path 数据格式保存模板路径
# 新建templet.xlsx文件
# 写入文件的时候需求提前关闭掉
# 不能同时登录两个证券账号


import json
import sys

from selenium import webdriver
from selenium.webdriver.common.by import By

import smtplib
import eyed3
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from pydub import AudioSegment
import email
import imaplib
import os
import shutil
import time
import traceback
import zipfile
from email.header import decode_header, Header
from email.utils import parseaddr
from pathlib import Path
import datetime
import datedays
from selenium.webdriver.common.keys import Keys
import pandas as pd
import baostock as bs
import eventlet  # 导入eventlet这个模块
from chinese_calendar import is_workday


DEBUG = 1
PC_COM = 1  # 0-家庭 1-公司

Stock_url = "https://gu.qq.com/i/#/1/all/mk"  # 检索地址
Check_space = 30  # 间隔30分钟
Check_time = 14  # 策略判断时间 14点后

if PC_COM == 1:
    Stock_data_path = "D:\\video\\tool\\auto_tool\\a money\\strategy"  # 数据保存路径
    Stock_templet_path = "D:\\video\\tool\\auto_tool\\a money\\strategy\\templet.xlsx"  # 模板路径
else:
    Stock_data_path = "D:\\video\\tool\\auto_tool\\a money\\data"  # 数据保存路径
    Stock_templet_path = "D:\\video\\tool\\auto_tool\\a money\\data\\templet.xlsx"  # 模板路径

remote_server_url = 'imap.qq.com'
email_url = "3324564761@qq.com"  # 当前使用邮箱
password = "ioaeproulegrchff"
email_msg_to = "1984832368@qq.com"  # 发送邮箱
check_time = 10 # n 秒检测一次是否更新

pc_name = ["com", "fam"] # 运行 pc 环境

rewrite_print = print
def print(*arg):
   rewrite_print(*arg)
   output_dir = "log_file"
   if not os.path.exists(output_dir):
         os.makedirs(output_dir)
   log_name = 'log.txt' # 日志文件名称
   filename = os.path.join(output_dir, log_name)
   rewrite_print(*arg,file=open(filename,"a")) # 写入文件

def print_d(*arg): #仿真调试用
    if DEBUG == 1:
        print('------调试打印开始-----')
        print(*arg)
        print('------调试打印结束-----\r\n')



class doc_function():  # 文件操作
    def save_data_txt(data):  # 添加数据到文件
        info_str = str(data)
        info_str += '\r\n'
        print(info_str)
        with open('flash_tool.log', 'a') as lead:
            lead.write(info_str)

    def set_data(data):  # 重写文件
        data_w = "data  " + data
        fi = open('data.txt', 'w', encoding='utf-8')
        fi.write(data_w)

    def mkdir(path):  # 新建文件夹
        folder = os.path.exists(path)

        if not folder:  # 判断是否存在文件夹如果不存在则创建为文件夹
            os.makedirs(path)  # makedirs 创建文件时如果路径不存在会创建这个路径
            print("---  new folder...  ---")
        else:
            print("folder is:", path)

    def copyfile(srcfile, dstpath):  # 复制文件
        if not os.path.isfile(srcfile):
            print("%s not exist!" % (srcfile))
        else:
            fpath, fname = os.path.split(srcfile)  # 分离文件名和路径
            if not os.path.exists(dstpath):
                os.makedirs(dstpath)  # 创建路径
            shutil.copy(srcfile, dstpath + fname)  # 复制文件

    def file_zip(start_file, zip_dir):  # 压缩文件
        # start_dir 要压缩的文件
        # zip_file 输出zip文件的路径
        fpath_start_dir, fname = os.path.split(start_file)  # 分离文件名和路径

        zip_file = zip_dir + '.zip'
        z = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED)
        print(z)
        for path, dirname, file_name in os.walk(fpath_start_dir):
            # print("文件夹根路径：", path)
            fpath = path.replace(fpath_start_dir, '')  # 去除根路径名称
            # print("--去除根路径：", fpath)
            fpath = fpath and fpath + os.sep  # 在原fpath加上\
            # print("****去除根路径+\ ：", fpath)

            for filename in file_name:  # 逐个循环读取文档名称
                if filename.find(fname) >= 0:
                    # print(filename)
                    z.write(os.path.join(path, filename), fpath + filename)  # 实现在输出路径的Zip压缩操作
        z.close()


    # def get_web():
        # if os.path.isfile("cfg.txt"):
        #     f = open(r"cfg.txt", "r", encoding="utf-8")
        #     data = f.read()
        #     reg = 'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
        #     url = re.findall(reg, data)
        #     return url[-1]  # 假如地址在这一行的最后一个位置
        # else:
        #     print(" cfg.txt is not accessible, please creat!!!")
        #     sys.exit()

    def file_unzip_download(unzip_file):
        print(f"开始进行解压缩并下载···")

        zip_file = zipfile.ZipFile(unzip_file)
        fpath, fname = os.path.split(unzip_file)
        out_file = fpath + "\\unzip"
        if os.path.isdir(out_file):
            shutil.rmtree(out_file)
            os.mkdir(out_file)
        else:
            os.mkdir(out_file)
        for names in zip_file.namelist():
            zip_file.extract(names, out_file)
        zip_file.close()
        folder = Path(out_file.strip())
        result = list(folder.rglob(f'*{".hex"}*'))  # 只要hex文件
        print(result[0])  # 默认一个压缩包只有一个文件
        flash_file = str(result[0])
        # 进行文件的各种操作处理
        shutil.rmtree(out_file)

    def search_file(key_word, self_path=None):  # 查找的关键字 self_path是自定义路径
        if self_path != None:
            folder_str = self_path
        else:
            folder_str = os.path.join(os.getcwd(), qq_email_rx.Subject)  # 刘森 新世界

        folder = Path(folder_str.strip())
        # print(folder)
        if folder.exists():
            print("开始根据关键字查找文件···")
        else:
            print("未找到存放关键字对应文件的文件夹")
            return None

        result = list(folder.rglob(f'*{key_word}*'))
        if not result:
            print(f'在{folder} 下未找到关键字名称 {key_word} 的文件夹或文件')
            return None
        else:
            print(f'在{folder} 下找到关键字名称 {key_word} 的文件夹或文件{result}')
            return result

    def get_stock_code():  # 获取股票代码
        df = pd.read_excel(Stock_code_path, sheet_name=0, dtype={0:"string"})
        code_list = df["stock_code"].tolist()

        # 去掉中间的空项
        res = []
        for val in code_list:
            if str(val) != '<NA>':
                res.append(str(val))

        return res

    def get_str_row_column(words): #获取字符串中行数和列的最大字符数，返回两者的最大值
        row = words.count("\n") + 1

        # 存储所有的相同字符之间的距离
        length = []
        loc = 0
        last_loc = 0

        while words.find("\n", loc+1) != -1:
            loc = words.find("\n", loc+1)

            length.append(loc - last_loc)
            last_loc = loc

        length.append(len(words) - last_loc) # 获取最后一行长度
        # print(length)
        # print(f'最大长度：{max(length)}')
        column = max(length)
        # print(max(row, column))
        return max(row, column)

    def delete_files(file_path):
        if os.path.exists(file_path):
            shutil.rmtree(file_path)
            return True
        else:
            # file not found message
            print(f'要删除的文件夹{file_path}找不到')
            return False

    def cookie_creat(pc_name = pc_name[0]):  # 生成cook platform是投稿平台 pc_name是哪个pc
        web = webdriver.Chrome()
        web.get(Stock_url)

        web.delete_all_cookies()  # 先删除cookies
        time.sleep(60)  # 这个时间用于手动登录,扫码登录可以适当缩短这个等待时间
        dictcookies = web.get_cookies()  # 读取登录之后浏览器的cookies
        jsoncookies = json.dumps(dictcookies)  # 将字典数据转成json数据便于保存

        path_keyword = pc_name
        search_path = os.path.join(os.getcwd(), "cookies")
        doc_function.mkdir(search_path)
        save_name = search_path + "\\" + path_keyword + ".txt"
        print_d(save_name)

        with open(save_name, 'w') as f:  # 写进文本保存
            f.write(jsoncookies)
        print('cookie is ok')

    def cookie_use(pc_name = pc_name[0]):  # 使用cookie进行登录 返回web
        web = webdriver.Chrome()
        web.implicitly_wait(60)  # 隐性等待，最长等30秒


        web.get(Stock_url)

        path_keyword = pc_name
        search_path = os.path.join(os.getcwd(), "cookies")
        cook_name = search_path + "\\" + path_keyword + ".txt"

        if os.path.isfile(cook_name):
            f = open(cook_name, 'r')
            listcookie = json.loads(f.read())  # 读取文件中的cookies数据

            for cookie in listcookie:
                web.add_cookie(cookie)  # 将cookies数据添加到浏览器
            return web
            # web.refresh()  # 刷新网页
        else:
            # print("不存在此文件", cook_name)
            return web

        # input()  # 暂时暂停 否则一闪而过 后面有内容了再去掉

    def add_row_to_excel(data_row, excel_path): # 向excel中新加一行数据 data_row：行数据  excel_pat：路径
        print_d(data_row)
        df_all = pd.DataFrame(data=None)  # 创建空的 dataframe
        # 多数据做循环处理即可
        df = pd.read_excel(excel_path, header=0, sheet_name='Sheet1')  # 读入数据
        df_all = pd.concat([df_all, df], ignore_index=True)  # concat 合并有相同字段名的dataframe
        df = pd.DataFrame(data_row)
        df_all = pd.concat([df_all, df], ignore_index=True)

        data_list = df_all.drop_duplicates(keep='first')  # 避免字段名重复写入，一般会做去重处理
        df_all.to_excel(excel_path, index=False, encoding='utf-8')  # 写出数据



class web_tx_parse:  # 腾讯的网页处理
    def __init__(self, pc):
        self.pc_name = pc   # pc_name[0] - com;   [1] - fam
        self.web = None

    def log_on(self):  # 登录
        print("登录中")
        web = doc_function.cookie_use(self.pc_name)
        web.refresh()  # 必需在这刷新，否则闪退
        self.web = web
        time.sleep(2)
        self.web.find_elements(By.XPATH, '/html/body/div[5]/div/div[3]')[0].click() # 将 我知道 点掉
        time.sleep(1)


    def data_signal(self, group):  # 单组数据获取  group:组web元素
        print(self.pc_name, "-组信息获取中-组名：", group.text)

        sheet_name = group.text
        stock_data_list = []  # 数据
        stock_name_list = []  # 名字
        stock_new_list = []  # 最新价
        stock_open_list = []  # 开盘

        # 获取股票信息 代码 名字 最新价 今开
        group.click()
        time.sleep(2)  # 必需等待
        stock_name = self.web.find_elements(By.XPATH, '//*[@id="zxg-body"]/div[5]')[0].text
        stock_data_list = stock_name.split()
        print_d(len(stock_data_list))
        stock_num = len(stock_data_list)/14
        stock_num = int(stock_num)
        for i in range(0, stock_num): # 目前一组数据是14个元素
            num = i * 14
            name_str = stock_data_list[0+num] + stock_data_list[1+num]  # 代码+名字
            stock_name_list.append(name_str)
            stock_new_list.append(stock_data_list[2+num])
            stock_open_list.append(stock_data_list[6+num])

        print(stock_name_list, stock_new_list, stock_open_list)  # ['000925众合科技', '300631久吾高科'] ['7.82', '33.29'] ['7.63', '33.00']
        input()
        # //*[@id="sz000925"]/dt/div[2]/div[2]/a
        # //*[@id="sz300631"]/dt/div[2]/div[2]/a

        #//*[@id="sz000925"]/dt/div[2]/div[1]/a
        # //*[@id="sz000925"]/dt/div[1]    //*[@id="zxg-body"]/div[5]

        df = pd.read_excel(Stock_templet_path, sheet_name=sheet_name, dtype={0: "string"})
        # stock_title = df.columns.values
        # stock_title_str = "".join('%s-' %a for a in stock_title)
        # if stock_title_str.find(stock_name) == -1: # 没有发现这只票  认为是初次数据录入  这个是一直执行的
        #     print(stock_name)
        # 直接组包 写入


        # 后续数据录入  这个是每天只录入一次 前面带时间

        stock_signal_data = pd.DataFrame({'时间' :['1月','2月' ,'3月' ,'4月'], '销量' :[10000,28989,67890,23453]})


        with pd.ExcelWriter(Stock_templet_path, mode='a', engine='openpyxl') as writer:
            stock_signal_data.to_excel(writer, sheet_name = sheet_name)

        # 策略执行
        if datetime.datetime.now().hour > Check_time:  # 14点后才进行策略判断
            stock_parse.stop_loss(Stock_templet_path)

        # input()

        # 获取信息并将信息保存
        # 输入代码号
        search_web = self.web.find_elements(By.XPATH, '//*[@id="root"]/div/div/div[2]/div[1]/form/div[1]/input')[0]
        search_web.send_keys(Keys.CONTROL, "a")
        search_web.send_keys(stock_code)
        time.sleep(2)
        search_web.send_keys(Keys.ENTER)
        time.sleep(1)

        news_xpath = '//*[@id="content_wrap"]/div[2]/div/div[4]/div/div[9]/div[1]/div/ul/li[1]/a'
        public_xpath = '//*[@id="content_wrap"]/div[2]/div/div[4]/div/div[9]/div[2]/div/ul/li[1]/a'
        news_data = self.web.find_elements(By.XPATH, news_xpath)[0]  # 新闻
        public_data = self.web.find_elements(By.XPATH, public_xpath)[0] #公告
        data_row = {"新闻": [news_data.get_attribute('title')], "新闻链接": [news_data.get_attribute('href')],"公告": [public_data.get_attribute('title')], "公告链接": [public_data.get_attribute('href')]}
        data_row_last = data_row
        # 进行信息保存
        stock_file_path = Stock_data_path + "\\" + stock_code + ".xlsx"
        if doc_function.search_file(stock_code, Stock_data_path) == None: # 这个文件没有则从模板新建 只保存一次
            shutil.copy(Stock_templet_path, stock_file_path)
            doc_function.add_row_to_excel(data_row, stock_file_path)  # 保存
        else: # 一直保存到最新消息
            df = pd.read_excel(stock_file_path, sheet_name=0, dtype={0: "string"})
            excel_row = df.iloc[-1].to_dict()

            # 新闻更新检索
            news_num = 1
            stock_update_list = []
            while data_row.get("新闻")[0] != excel_row.get("新闻") and news_num < 5: # 新闻发送变化 最大向前找5个
                last_str = "li[" + str(news_num)
                news_num = news_num + 1
                now_str = "li[" + str(news_num)
                news_xpath = news_xpath.replace(last_str, now_str)
                # print_d(news_xpath)

                news_data = self.web.find_elements(By.XPATH, news_xpath)[0]  # 新闻
                data_row = {"新闻": [news_data.get_attribute('title')], "新闻链接": [news_data.get_attribute('href')],"公告": [public_data.get_attribute('title')], "公告链接": [public_data.get_attribute('href')]}
                doc_function.add_row_to_excel(data_row, stock_file_path)  # 保存
                stock_update_list.append(data_row)

            # 邮箱发送
            if news_num == 1:
                print(stock_code, "------未发生新闻信息更新------")
            else:
                doc_function.add_row_to_excel(data_row_last, stock_file_path)  # 将最新的保存为最后一个
                stock_update_list.append(data_row)
                # 进行数据滤除和邮箱发送
                email_data = []
                for dict in stock_update_list:
                    if dict.get("新闻")[0].find(key_word) != -1:
                        email_data.append(dict.get("新闻"))
                        email_data.append(dict.get("新闻链接"))

                print_d(email_data)
                if email_data != []:
                    send_email(email_url, password, email_msg_to, f'{stock_code} 新闻信息更新', " ".join('%s-' %a for a in email_data))
                    print(stock_code, "------发生新闻信息更新，已将更新信息发到邮箱------")
                else:
                    print(stock_code, "------发生新闻信息更新，但未发现关键字------")


        # input()
    def get_groups(self):
        # //*[@id="zxg-body"]/div[4]/div[1]/div/ul/li[2]/div
        stock_groups_list = []
        for num in range(2, 7): # 检索2-7
            xpath = "//*[@id=\"zxg-body\"]/div[4]/div[1]/div/ul/li[" + str(num) + "]/div"
            data = self.web.find_elements(By.XPATH, xpath)[0]
            data_num = data.text
            if data_num.isdigit(): # 只进行数字组的获取
                stock_groups_list.append(data)

        print("股票分组列表", stock_groups_list)  #
        return stock_groups_list


    def data_all(self):  # 获取所有的数据信息
        if stock_parse.stock_work_day() == False:
            return None

        eventlet.monkey_patch()  # 必须加这条代码
        group_name_list = stock_get.get_groups()

        for group in group_name_list:
            try:
                with eventlet.Timeout(20, False):  # 设置超时时间为20秒
                    try:
                        stock_get.data_signal(group)
                        time.sleep(1)
                        # 该语句正常执行
                    except Exception:
                        print(f'-------进行信息检查{group}出错-----\r\n')
            except Exception:
                print("----时间处理超时-----")

            break  # 后续去掉


    def quit(self):  # 退出
        print(self.platform, "退出中")



class stock_parse:  # 股票处理
    def __init__(self, data_path):
        self.data_path = data_path

    def is_trade_day(date):  # 得到当前日期是否为股票交易日
        if is_workday(date):
            if date.isoweekday() < 6:
                return True
        return False

    def stock_work_day(group_name = None): # 判断现在是否是交易时间 或者获取下一个工作日
        if group_name == None: # 判断当前时间是否是交易时间
            data_temp = time.strftime('%Y-%m-%d', time.localtime())
            data = datetime.datetime.strptime(data_temp, '%Y-%m-%d').date()
            if stock_parse.is_trade_day(data) == False:
                print("非交易日期")
                return False
            elif datetime.datetime.now().hour < 9 or datetime.datetime.now().hour > 21: # 15
                print("非交易时间")
                return False
            else:
                print("交易时间,开始进行数据获取")
                return True
        else: # 判断组号所代表的那一天是否是交易日
            days_num = 1
            while stock_parse.is_trade_day(datedays.gettomorrow(days=days_num)) == False:
                days_num = days_num + 1

            return str(datedays.gettomorrow(days=days_num))[-2:]  # 返回下一个组名


    def get_stock_codes(date=None): # 获取指定日期的A股代码列表  stock_codes = get_stock_codes()
        """
        获取指定日期的A股代码列表

        若参数date为空，则返回最近1个交易日的A股代码列表
        若参数date不为空，且为交易日，则返回date当日的A股代码列表
        若参数date不为空，但不为交易日，则打印提示非交易日信息，程序退出

        :param date: 日期
        :return: A股代码的列表
        """

        # 登录baostock
        bs.login()

        # 从BaoStock查询股票数据
        stock_df = bs.query_all_stock(date).get_data()

        # 如果获取数据长度为0，表示日期date非交易日
        if 0 == len(stock_df):

            # 如果设置了参数date，则打印信息提示date为非交易日
            if date is not None:
                print('当前选择日期为非交易日或尚无交易数据，请设置date为历史某交易日日期')
                sys.exit(0)

            # 未设置参数date，则向历史查找最近的交易日，当获取股票数据长度非0时，即找到最近交易日
            delta = 1
            while 0 == len(stock_df):
                stock_df = bs.query_all_stock(datetime.date.today() - datetime.timedelta(days=delta)).get_data()
                delta += 1

        # 注销登录
        bs.logout()

        # 筛选股票数据，上证和深证股票代码在sh.600000与sz.39900之间
        stock_df = stock_df[(stock_df['code'] >= 'sh.600000') & (stock_df['code'] < 'sz.399000')]

        # 返回股票列表
        return stock_df['code'].tolist()

    def stop_loss(data_path): # 止损策略 data_path：excel路径
        # code_list = doc_function.get_stock_code()  # 获取股票代码
        # print(code_list)
        print(data_path)

    def target_profit(data_path): # 止盈策略
        print(data_path)



if __name__ == "__main__":
    print("stock for money !!!!")
    # doc_function.cookie_creat()

    # df = pd.read_excel(Stock_templet_path, sheet_name="templet_short", dtype={0: "string"})
    # print(df.columns.values)  # df.columns.values 标题行 ['时间' 'A价格' 'A百分比' 'B价格' 'B百分比']

    check_num = 0
    stock_get = web_tx_parse(pc_name[0])
    stock_get.log_on()
    stock_get.data_all()
    # while 1:
    #     try:
    #         print('\r\n\r\n', datetime.datetime.now())
    #         check_num = check_num + 1
    #         print(f'进行第{check_num}次轮询检查\r\n')
    #         stock_get.data_all()
    #         print(f'\r\n第{check_num}次轮询检查结束\r\n')
    #         time.sleep(60 * Check_space)
    #     except:
    #         print(f'第{check_num}次轮询检查出现错误\r\n')
    #         traceback.print_exc()
    #         continue








